// *************************************************************************************************
//
// MK3D Engine
// --------------------------------------
// Copyright (C) 2007-2011 Zeb (mailto: zebbey@gmail.com)
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// *************************************************************************************************

#ifndef __H_MK_D3D9_RENDER_DEVICE__
#define __H_MK_D3D9_RENDER_DEVICE__

#include <d3d9.h>
#pragma comment(lib, "d3d9.lib")

#include "mk_render_device.h"

namespace MK3DE
{
	class CD3D9RenderDevice : public CRenderDevice
	{
	public:
		CD3D9RenderDevice();
		~CD3D9RenderDevice();

	public:
		//! Create render device.
		/**
		\param winHandle : Main window handle.
		\return If the method succeeds, the return value is TRUE. If the method fails, the 
		return value is FALSE.
		*/
		BOOL Create(void* winHandle);

		//! Begin to render a frame.
		/**
		Before render a frame, this method must be called. If the method failed, the later 
		render operate will fail.
		\return : If the method succeeds, the return value is TRUE. If the method fails, the 
		return value is FALSE.
		*/
		BOOL BeginFrame();

		//! End to render a frame.
		/**
		After render a frame, this method must be called.
		\return : If the method succeeds, the return value is TRUE. If the method fails, the 
		return value is FALSE.
		*/
		BOOL EndFrame();

		//! Clear buffer.
		BOOL Clear(BOOL clearBackBuffer, BOOL clearDepth, BOOL clearStencil, DWORD backBufferColor, float depth, DWORD stencil);

		//! Present back buffer to screen.
		/**
		This method put back buffer to screen display buffer.
		\param pwin : Target render window.
		\return : If the method succeeds, the return value is TRUE. If the method fails, the 
		return value is FALSE.
		*/
		BOOL Present(CRenderWindow* pwin);

		//! Set current view port.
		/**
		\param x : Together with y, value describing the coordinate of the upper-left corner of
		the viewport.
		\param y : Together with x, value describing the coordinate of the upper-left corner of
		the viewport
		\param Width : Together with Height, value describing the range of the viewport.
		\param Height : Together with Width, value describing the range of the viewport.
		\param minZ : Together with maxZ, value describing the range of depth values into which 
		a scene is to be rendered, the minimum and maximum values of the clip volume.
		\param maxZ : Together with minZ, value describing the range of depth values into which 
		a scene is to be rendered, the minimum and maximum values of the clip volume
		\return : If the method succeeds, the return value is TRUE. If the method fails, the 
		return value is FALSE.
		*/
		BOOL SetViewport(DWORD x, DWORD y, DWORD Width, DWORD Height, float minZ, float maxZ);
		void GetViewport(DWORD& x, DWORD& y, DWORD& Width, DWORD& Height, float& minZ, float& maxZ);

		//! Reisze notification when the size of render target window has changed.
		/**
		The method should be called whenever the size of render target window has changed.
		\param pwin : Render target window.
		\return : If the method succeeds, the return value is TRUE. If the method fails, the 
		return value is FALSE.
		*/
		BOOL OnResize(CRenderWindow* pwin);

		//! Get back buffer dimension.
		void GetBackBufferDimension(DWORD& width, DWORD& height);

		//! Get back buffer format.
		PIXEL_FORMAT GetBackBufferFormat();

		//! Create and destroy render input.
		BOOL CreateRenderInput(RenderInputElemDesc* desc, DWORD descNum, CRenderInput*& renderInput);
		void DestroyRenderInput(CRenderInput*& pRenderInput);

		//! Create and destroy vertex buffer.
		BOOL CreateVertexHardwareBuffer(DWORD vertexSize, DWORD vertexNum, HARDWARE_BUFFER_USAGE usage, CHardwareBuffer*& buffer);
		void DestroyVertexHardwareBuffer(CHardwareBuffer*& pBuffer);

		//! Create and destroy index buffuer.
		BOOL CreateIndexHardwareBuffer(DWORD indexNum, INDEX_TYPE indexType, HARDWARE_BUFFER_USAGE usage, CHardwareBuffer*& buffer);
		void DestroyIndexHardwareBuffer(CHardwareBuffer*& pBuffer);

		//! Create and destroy 2d texture.
		BOOL CreateTexture2DFromFile(const char* fileName, CTexture2D*& pTexture);
		BOOL CreateTexture2DFromMemory(const BYTE* buffer, DWORD size, CTexture2D*& pTexture);
		BOOL CreateTexture2D(DWORD width, DWORD height, PIXEL_FORMAT format, DWORD mip, TEXTURE_USAGE usage, CTexture2D*& pTexture);
		void DestroyTexture2D(CTexture2D*& pTexture2D);

		//! Create and destroy 3d texture.
		BOOL CreateTexture3DFromFile(const char* fileName, CTexture3D*& pTexture);
		void DestroyTexture3D(CTexture3D*& pTexture);

		//! Create and destroy shader.
		BOOL CreateShaderFromFile(const char* fileName, CShader*& shader);
		BOOL CreateShaderFromMemory(const BYTE* buffer, DWORD size, CShader*& shader);
		void DestroyShader(CShader*& pShader);

		//! Create render target.
		BOOL CreateRenderTarget(DWORD width, DWORD height, PIXEL_FORMAT format, BOOL bDSBuffer, CRenderTarget*& pRenderTarget);
		void DestroyRenderTarget(CRenderTarget*& pRenderTarget);

		//! Bind render input for current rendering.
		BOOL BindRenderInput(CRenderInput* renderInput);

		//! Render primitives.
		BOOL DrawPrimitive(PRIMITIVE_TYPE primitiveType, DWORD firstVertex, DWORD minIndex, DWORD vertexNum, DWORD firstIndex, DWORD primitiveNum);

		//! Set textures for current rendering.
		BOOL SetTexture(DWORD stage, CTexture2D* pTexture);

		//! Manage render target.
		/**
		\param pRenderTarget : new render target. To reset to default render target, pass NULL.
		\return : Succeed or not.
		*/
		BOOL SetRenderTarget(CRenderTarget* pRenderTarget);

		//! Set render state.
		BOOL SetRenderState(RENDER_STATE state, DWORD value);

		//! Get the max texture size supported by current render device.
		DWORD GetMaxTextureSize();

		//! Manage clip plane. 
		BOOL SetClipPlane(DWORD index, D3DXPLANE& plane, BOOL enable);
		BOOL EnableClipPlane(DWORD index, BOOL enable);

	public:
		LPDIRECT3DDEVICE9 GetD3DDevice() { return _pd3d9device; }
		const D3DPRESENT_PARAMETERS& GetD3DPresentParameters() const { return _d3dpp; }
		BOOL IsStencilBuffer() { return _bStencilBuffer; }

	private:
		void __SetDefaultRenderState();
		BOOL __CheckDepthStencilBufferFormat();
		BOOL __CreateDevice(HWND hWnd);

	private:
		LPDIRECT3D9         _pd3d9;
		LPDIRECT3DDEVICE9   _pd3d9device;

		D3DPRESENT_PARAMETERS   _d3dpp;
		D3DCAPS9                _d3dcaps;

		BOOL _bStencilBuffer;

		D3DVIEWPORT9        _viewport;

		BOOL _useIndexBuffer;

		LPDIRECT3DSURFACE9 _prevRenderTarget;
		LPDIRECT3DSURFACE9 _prevDSSurface;
		D3DVIEWPORT9        _prevViewport;
	};
};

#endif // __H_MK_D3D9_RENDER_DEVICE__